package sample.algo.sistr.algosample.algos.arrays;

import android.content.Context;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;

import java.security.InvalidParameterException;
import java.util.Collections;
import java.util.Comparator;
import java.util.Random;
import java.util.Vector;

import sample.algo.sistr.algosample.R;
import sample.algo.sistr.algosample.algos.Algo;

public class Algo02   extends Algo {

    private final static String TAG = "algosample";

    private TextView textViewTestResult;
    private EditText editText;
    private EditText editTextValue;
    /*
    Input : an array of int and a target value X
    Return : the INDEX of 2 integers of that array that sum to X, or null if no pair sum to 0
    Example:
        - Input : [0, 3, -3, 5, 7, -9, 10] target value : 7
        - return : {0, 4} or {2, 6}
    */

    public Algo02(Context context) {
        super("2 sum X", context);
        textViewTestResult = null;
        editText = null;
    }

    @Override
    public View GetDisplay() {
        ViewGroup root = (ViewGroup) ((LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE)).inflate(R.layout.algo_array_02, null);

        textViewTestResult = (TextView)root.findViewById(R.id.textViewTest);

        Button buttonTest = (Button)root.findViewById(R.id.buttonTest);
        buttonTest.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                StringBuilder testResult = new StringBuilder();

                // We build a set of array to test, associated to a set of random values to find
                int numberOfRandomTest = 25;
                int testArrays[][] = new int[numberOfRandomTest][];
                int testValues[] = new int[numberOfRandomTest];

                Random r = new Random();
                int randomRange = 9000000;
                int numberOfElementsInArray = 12000;
                for(int i = 0 ; i < numberOfRandomTest ; i++){
                    testArrays[i] = new int[numberOfElementsInArray];
                    for(int j = 0 ; j < numberOfElementsInArray ; j++){
                        testArrays[i][j] = r.nextInt(randomRange*2+1)-randomRange;
                    }
                    testValues[i] = r.nextInt(randomRange*2+1)-randomRange;
                }

                long startTime = System.currentTimeMillis();
                int testFailed = 0;
                int testSucceed = 0;
                // We test with the brute force algorythm
                for(int i = 0 ; i < numberOfRandomTest ; i++) {
                    int res[] = function0(testArrays[i], testValues[i]);

                    if(res.length == 2) {
                        if(testArrays[i][res[0]] + testArrays[i][res[1]] != testValues[i]) {
                            Log.w(TAG, "test[" + i + "] Fail");
                            testFailed++;
                        } else {
                            testSucceed++;
                        }
                    } else {
                        testFailed++;
                        Log.e(TAG, "Error test[" + i + "]");
                    }
                }
                long endTime = System.currentTimeMillis();
                testResult.append("Brute force algo : execution time ").append((endTime-startTime)).append("ms\n");
                testResult.append("Brute force algo : number of fail ").append(testFailed).append("\n");
                testResult.append("Brute force algo : number of success ").append(testSucceed).append("\n");

                startTime = System.currentTimeMillis();
                testFailed = 0;
                testSucceed = 0;
                // We test with the first optimisation of the algo
                for(int i = 0 ; i < numberOfRandomTest ; i++) {
                    int res[] = function1(testArrays[i], testValues[i]);

                    if(res.length == 2) {
                        if(testArrays[i][res[0]] + testArrays[i][res[1]] != testValues[i]) {
                            Log.w(TAG, "test[" + i + "] Fail");
                            testFailed++;
                        } else {
                            testSucceed++;
                        }
                    } else {
                        testFailed++;
                        Log.w(TAG, "Strange Fail test[" + i + "]");
                    }
                }
                endTime = System.currentTimeMillis();
                testResult.append("Algo optim 1 : execution time ").append((endTime-startTime)).append("ms\n");
                testResult.append("Algo optim 1 : number of fail ").append(testFailed).append("\n");
                testResult.append("Algo optim 1 : number of success ").append(testSucceed).append("\n");

                if(textViewTestResult != null)
                    textViewTestResult.setText(testResult);
            }
        });

        editText = (EditText) root.findViewById(R.id.editText);
        editText.setText("[6, -5, 3, 11, 24, -54, -1, 0, -17]");

        editTextValue = (EditText) root.findViewById(R.id.editTextValue);
        editTextValue.setText("6");

        Button buttonRun = (Button)root.findViewById(R.id.buttonRun);
        buttonRun.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                StringBuilder testResult = new StringBuilder();

                // TODO handle user inputs !

                if(textViewTestResult != null)
                    textViewTestResult.setText(testResult);
            }
        });

        return root;
    }

    // Brute force algorithm with a double for loop
    private int [] function0(int array[], int value) {
        if(array == null)
            throw new InvalidParameterException("input can't be null !");
        int res[] = null;

        if(array.length > 1){
            for(int i = 0 ; i < array.length-1 && res == null ; i++){
                for(int j = i+1 ; j < array.length ; j++){
                    if(array[i] + array[j] == value) {
                        res = new int[]{i, j};
                        break;
                    }
                }
            }

        }

        return res;
    }

    // First sort the array (on a copy, and keep index reference), then look for a couple in an optimised way
    private int [] function1(int array[], int value) {
        if(array == null)
            throw new InvalidParameterException("input can't be null !");
        int res[] = null;

        // We copy the input array
        Vector<Integer[]> a = new Vector<Integer[]>(array.length);
        for(int i = 0 ; i < array.length ; i++){
            a.add(new Integer[]{array[i],i});
        }

        // We sort the array
        Collections.sort(a, new Comparator<Integer[]>() {
            @Override
            public int compare(Integer[] i, Integer[] i2) {
                return i[0].compareTo(i2[0]);
            }
        });

        int indexEnd = a.size()-1;

        for(int i = 0 ; i < indexEnd && res == null ; i++){
            for(int j = indexEnd ; j > i ; j--){
                int sum = a.get(i)[0] + a.get(j)[0];
                if(sum > value){
                    // array[j] == array[indexEnd] is eliminated from possible right values
                    indexEnd--;
                } else if(sum < value){
                    // we need to get a higher left value
                    break;
                } else {
                    // found !
                    res = new int[]{a.get(i)[1], a.get(j)[1]};
                    break;
                }
            }
        }

        return res;
    }
}